package com.andy.blog.admin.service.impl;

import com.andy.blog.admin.dto.ArticleDTO;
import com.andy.blog.admin.dto.ArticleHomeDTO;
import com.andy.blog.admin.dto.ArticleHomeDetailDTO;
import com.andy.blog.admin.dto.ArticleRecommendDTO;
import com.andy.blog.admin.dto.ArticleTagInfoDTO;
import com.andy.blog.admin.dto.CategoryDTO;
import com.andy.blog.admin.dto.TagDTO;
import com.andy.blog.admin.service.ArticleBusService;
import com.andy.blog.admin.service.LikeBusService;
import com.andy.blog.common.constant.CacheConstants;
import com.andy.blog.common.core.redis.RedisCache;
import com.andy.blog.common.enums.DeleteTypeEnum;
import com.andy.blog.common.exception.BlogServiceException;
import com.andy.blog.common.page.PageUtilsDTO;
import com.andy.blog.common.reponse.BlogResultCode;
import com.andy.blog.common.utils.SecurityUtils;
import com.andy.blog.common.utils.TransformUtils;
import com.andy.blog.dal.domain.ArticleDO;
import com.andy.blog.dal.domain.ArticleTagDO;
import com.andy.blog.dal.domain.ArticleTagInfoDO;
import com.andy.blog.dal.domain.CategoryDO;
import com.andy.blog.dal.domain.TagDO;
import com.andy.blog.dal.service.ArticleDalService;
import com.andy.blog.dal.service.ArticleTagDalService;
import com.andy.blog.dal.service.CategoryDalService;
import com.andy.blog.dal.service.TagDalService;
import com.andy.blog.dal.vo.ArticleTagVO;
import com.andy.blog.dal.vo.ArticleVO;
import com.andy.blog.dal.vo.TagVO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * Created by andy on 2023/6/22 18:34
 */
@Service("articleBusService")
public class ArticleBusServiceImpl implements ArticleBusService, LikeBusService {


    /**
     * 用户点赞文章
     */
    public static final String USER_ARTICLE_LIKE = "user_article_like:";



    @Resource
    private ArticleDalService articleDalService;

    @Resource
    private ArticleTagDalService articleTagDalService;

    @Resource
    private CategoryDalService categoryDalService;

    @Resource
    private TagDalService tagDalService;

    @Resource
    private RedisCache redisCache;



    @Override
    public PageUtilsDTO<ArticleTagInfoDTO> selectListByPage(ArticleVO articleVO) {

        // 1. 获取文章分页数据
        articleVO.setIsDeleted(DeleteTypeEnum.DELETE_N.getCode());
        IPage<ArticleDO> articleDOIPage = articleDalService.selectListByPage(articleVO);
        List<ArticleDO> articleList = articleDOIPage.getRecords();

        List<ArticleTagInfoDTO> articleTagInfoDTOS = TransformUtils.transformList(articleList, ArticleTagInfoDTO.class);
        for (ArticleTagInfoDTO articleTagInfoDTO : articleTagInfoDTOS) {
            // 2. 获取分类
            CategoryDO categoryDO = categoryDalService.getById(articleTagInfoDTO.getCategoryId());
            articleTagInfoDTO.setCategoryName(categoryDO.getCategoryName());

            // 3. 获取标签
            List<ArticleTagDO> articleTagDOS = articleTagDalService.selectList(ArticleTagVO.builder().articleId(articleTagInfoDTO.getId()).build());
            List<Long> tagIdList = articleTagDOS.stream().map(ArticleTagDO::getTagId).collect(Collectors.toList());

            List<TagDO> tagDOS = tagDalService.selectBatchIds(tagIdList);
            articleTagInfoDTO.setTagList(TransformUtils.transformList(tagDOS, TagDTO.class));
        }

        return PageUtilsDTO.build(articleDOIPage.getTotal(), articleTagInfoDTOS);
    }

    @Override
    public List<ArticleDTO> selectList(ArticleVO articleVO) {
        List<ArticleDO> articleDOS = articleDalService.selectList(articleVO);
        return TransformUtils.transformList(articleDOS, ArticleDTO.class);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addArticle(ArticleVO articleVO) {
        // 1. 校验分类是否存在
        checkCategory(articleVO.getCategoryId());

        if (StringUtils.isBlank(articleVO.getArticleCover())) {
            throw new BlogServiceException(BlogResultCode.BLOG_000001, "入参不能为空");
        }

        // 2. 新增文章信息
        Long articleId = articleDalService.addArticle(articleVO);

        // 3. 新增标签和文章关系
        saveArticleTag(articleVO, articleId);
    }

    /**
     * 保存文章标签关系
     */
    private void saveArticleTag(ArticleVO articleVO, Long articleId) {
        List<Long> tagList = articleVO.getTagIdList();
        if (CollectionUtils.isEmpty(tagList)) {
            throw new BlogServiceException("文章标签不能为空");
        }

        for (Long tagId : tagList) {
            articleTagDalService.insertArticleTag(ArticleTagVO.builder().tagId(tagId).articleId(articleId).build());
        }
    }

    /**
     * 校验分类id是否存在
     */
    private void checkCategory(Long categoryId) {
        if (Objects.isNull(categoryId)) {
            throw new BlogServiceException("文章分类不能为空");
        }

        CategoryDO categoryDO = categoryDalService.getById(categoryId);
        if (Objects.isNull(categoryDO)) {
            throw new BlogServiceException("无此文章分类,不可提交");
        }
    }

    @Override
    public ArticleDTO selectArticleById(Long id) {
        ArticleDO articleDO = articleDalService.selectById(id);

        ArticleDTO articleDTO = TransformUtils.transform(articleDO, ArticleDTO.class);

        List<ArticleTagDO> tagDOS = articleTagDalService.selectList(ArticleTagVO.builder().articleId(id).build());

        List<Long> list = new ArrayList<>();
        for (ArticleTagDO tagDO : tagDOS) {
            list.add(tagDO.getTagId());
        }
        articleDTO.setTagIdList(list);

        return articleDTO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateArticle(ArticleVO articleVO) {

        ArticleDO articleDO = TransformUtils.transform(articleVO, ArticleDO.class);
        articleDO.setUpdateBy(SecurityUtils.getUsername());
        articleDalService.updateById(articleDO);

        articleTagDalService.deleteByArticleId(articleVO.getId());

        saveArticleTag(articleVO, articleVO.getId());
    }

    @Override
    public PageUtilsDTO<ArticleHomeDTO> listArticleHome(ArticleVO articleVO) {
        // 1. 获取分页的文章信息
        IPage<ArticleDO> articleTagInfoDTOIPage = articleDalService.listByPage(articleVO);
        List<ArticleDO> recordList = articleTagInfoDTOIPage.getRecords();

        // 获取所有tag标签
        List<TagDO> tagDOS = tagDalService.selectList(new TagVO());

        List<ArticleHomeDTO> dataList = new ArrayList<>();
        for (ArticleDO articleDO : recordList) {

            CategoryDO categoryDO = categoryDalService.getById(articleDO.getCategoryId());

            // 2. 获取文章关联的tag标签
            List<ArticleTagDO> articleTagDOS = articleTagDalService.selectList(ArticleTagVO.builder().articleId(articleDO.getId()).build());
            List<Long> tagIds = articleTagDOS.stream().map(ArticleTagDO::getTagId).collect(Collectors.toList());

            List<TagDO> articleTagDOs = tagDOS.stream().filter(s -> tagIds.contains(s.getId())).collect(Collectors.toList());

            ArticleHomeDTO articleTagInfoDTO = TransformUtils.transform(articleDO, ArticleHomeDTO.class);
            articleTagInfoDTO.setCategoryName(categoryDO.getCategoryName());
            articleTagInfoDTO.setCategory(TransformUtils.transform(categoryDO, CategoryDTO.class));
            articleTagInfoDTO.setTagList(TransformUtils.transformList(articleTagDOs, TagDTO.class));

            dataList.add(articleTagInfoDTO);
        }

        return PageUtilsDTO.build(articleTagInfoDTOIPage.getTotal(), dataList);
    }

    @Override
    public ArticleHomeDetailDTO getArticleHomeById(Long articleId) {
        ArticleTagInfoDO articleTagInfoDO = articleDalService.selectArticleHomeById(articleId);
        if (Objects.isNull(articleTagInfoDO)) {
            return new ArticleHomeDetailDTO();
        }

        ArticleHomeDetailDTO homeDetailDTO = TransformUtils.transform(articleTagInfoDO, ArticleHomeDetailDTO.class);
        // 浏览量+1
        redisCache.incrZet(CacheConstants.ARTICLE_VIEW_COUNT, articleId, 1D);

        Double zsetScore = redisCache.getZsetScore(CacheConstants.ARTICLE_VIEW_COUNT, articleId);
        Optional.ofNullable(zsetScore).map(Double::longValue).ifPresent(homeDetailDTO::setViewCount);

        // 查询点赞量
        Integer likeCount = redisCache.getHash(CacheConstants.ARTICLE_LIKE_COUNT, articleId.toString());
        homeDetailDTO.setLikeCount(Optional.ofNullable(likeCount).orElse(0));

        return homeDetailDTO;
    }


    @Override
    public List<ArticleRecommendDTO> listArticleRecommendVO() {
        ArticleVO articleVO = ArticleVO.builder()
                .isDeleted(DeleteTypeEnum.DELETE_N.getCode())
                .isRecommend("1")
                .status("1")
                .build();
        List<ArticleDO> articleDOS = articleDalService.selectList(articleVO);

        return TransformUtils.transformList(articleDOS, ArticleRecommendDTO.class)
                .stream()
                .limit(Math.min(articleDOS.size(), 3))
                .collect(Collectors.toList());
    }

    @Override
    public void addOrCancelLike(Long articleId) {
        boolean articleExist = articleDalService.checkArticleExist(articleId);
        if (BooleanUtils.isFalse(articleExist)) {
            throw new BlogServiceException("文章不存在");
        }

        // 用户id作为键，文章id作为值，记录用户点赞记录
        String userLikeArticleKey = USER_ARTICLE_LIKE + SecurityUtils.getUserId();
        // 判断是否点赞
        if (Boolean.TRUE.equals(redisCache.hasSetValue(userLikeArticleKey, articleId))) {
            // 取消点赞则删除用户id中的文章id
            redisCache.deleteSet(userLikeArticleKey, articleId);
            // 文章点赞量-1
            redisCache.decrHash(CacheConstants.ARTICLE_LIKE_COUNT, articleId.toString(), 1L);
        } else {
            // 点赞则在用户id记录文章id
            redisCache.setSet(userLikeArticleKey, articleId);
            // 文章点赞量+1
            redisCache.incrHash(CacheConstants.ARTICLE_LIKE_COUNT, articleId.toString(), 1L);
        }
    }
}
